package com.yytek.nmp.service.impl;

import java.util.*;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson2.JSON;
import com.yytek.common.client.consts.MqTopic;
import com.yytek.common.client.net.model.ServiceInvoke;
import com.yytek.common.client.net.model.SubServiceInvoke;
import com.yytek.common.core.constant.CacheConstants;
import com.yytek.common.core.constant.ProtocolConstans;
import com.yytek.common.core.constant.ValueConstant;
import com.yytek.common.core.domain.*;
import com.yytek.common.core.utils.DateUtils;
import com.yytek.common.core.utils.DriverUtil;
import com.yytek.common.core.utils.StringUtils;
import com.yytek.common.core.utils.uuid.IdUtils;
import com.yytek.common.core.web.domain.AjaxResult;
import com.yytek.common.redis.service.RedisService;
import com.yytek.nmp.api.RemoteUpgradeService;
import com.yytek.nmp.api.domain.UpgradeFirmware;
import com.yytek.nmp.domain.IotDeviceTopo;
import com.yytek.nmp.domain.IotServiceHistory;
import com.yytek.nmp.dto.DeviceInvkeParams;
import com.yytek.nmp.dto.DeviceServiceInvoke;
import com.yytek.nmp.fegin.MqttFegin;
import com.yytek.nmp.mapper.*;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yytek.nmp.domain.IotDeviceInfo;
import com.yytek.nmp.service.IIotDeviceInfoService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * 设备管理Service业务层处理
 * 
 * @author yytek-iot
 * @date 2022-07-05
 */
@Service
@Slf4j
public class IotDeviceInfoServiceImpl implements IIotDeviceInfoService
{
    @Resource
    private IotDeviceInfoMapper iotDeviceInfoMapper;
    @Resource
    private MqttFegin mqttFegin;
    @Resource
    private RedisService redisService;
    @Resource
    private IotDeviceTddbServiceImpl deviceTddbService;
    @Resource
    private IotDeviceTaskServiceImpl deviceTaskService;
    @Resource
    private IotDeviceCacheServiceImpl deviceCacheService;
    @Resource
    private IotEventHistoryMapper eventHistoryMapper;
    @Resource
    private IotServiceHistoryMapper serviceHistoryMapper;
    @Resource
    private IotDeviceTopoMapper deviceTopoMapper;
    @Resource
    private RemoteUpgradeService remoteUpgradeService;
    @Resource
    private RestTemplate restTemplate;
    /**
     * 查询设备管理
     * 
     * @param iotId 设备管理主键
     * @return 设备管理
     */
    @Override
    public IotDeviceInfo selectIotDeviceInfoByIotId(String iotId)
    {
        return iotDeviceInfoMapper.selectIotDeviceInfoByIotId(iotId);
    }

    /**
     * 查询设备管理列表
     * 
     * @param iotDeviceInfo 设备管理
     * @return 设备管理
     */
    @Override
    public List<IotDeviceInfo> selectIotDeviceInfoList(IotDeviceInfo iotDeviceInfo)
    {
        return iotDeviceInfoMapper.selectIotDeviceInfoList(iotDeviceInfo);
    }

    /**
     * 新增设备管理
     * 
     * @param iotDeviceInfo 设备管理
     * @return 结果
     */
    @Override
    public int insertIotDeviceInfo(IotDeviceInfo iotDeviceInfo)
    {
        iotDeviceInfo.setCreateTime(DateUtils.getNowDate());
        return iotDeviceInfoMapper.insertIotDeviceInfo(iotDeviceInfo);
    }

    @Override
    public int bindTopoDevice(String gateWayid, String subId) {
        IotDeviceTopo deviceTopo = new IotDeviceTopo();
        deviceTopo.setGatewayId(gateWayid);
        deviceTopo.setSubId(subId);
        deviceTopo.setBindTime(new Date());
        return deviceTopoMapper.insertIotDeviceTopo(deviceTopo);
    }

    /**
     * 网关解绑子设备
     * @param gateWayid
     * @param subId
     * @return
     */
    @Override
    public int noBindTopoDevice(String gateWayid, String subId) {
        return deviceTopoMapper.deleteTopoByGatewayIdAndSubId(gateWayid,subId);
    }

    /**
     * 查询设备相关缓存信息
     * @param id
     * @return
     */
    @Override
    public DeviceCache getDeviceCacheByIotId(String id) {
       return deviceCacheService.getDeviceCacheByIotId(id);
    }



    /**
     * 修改设备管理
     * 
     * @param iotDeviceInfo 设备管理
     * @return 结果
     */
    @Override
    public int updateIotDeviceInfo(IotDeviceInfo iotDeviceInfo)
    {
        iotDeviceInfo.setUpdateTime(DateUtils.getNowDate());
        return iotDeviceInfoMapper.updateIotDeviceInfo(iotDeviceInfo);
    }

    /**
     * 批量删除设备管理
     * 
     * @param iotIds 需要删除的设备管理主键
     * @return 结果
     */
    @Override
    public int deleteIotDeviceInfoByIotIds(String[] iotIds)
    {
        return iotDeviceInfoMapper.deleteIotDeviceInfoByIotIds(iotIds);
    }

    /**
     * 删除设备管理信息
     * 
     * @param iotId 设备管理主键
     * @return 结果
     */
    @Override
    public int deleteIotDeviceInfoByIotId(String iotId)
    {
        return iotDeviceInfoMapper.deleteIotDeviceInfoByIotId(iotId);
    }

    /**
     * 判断是否项目下存在设备
     * @param id 项目id
     * @return true存在,false不存在
     */
    @Override
    public boolean isExistsDeviceByProjectId(Long id) {
        //校验是否绑定设备,绑定删除不了
        List<IotDeviceInfo> list = selectIotDeviceInfoByProjectId(id);
        if(CollectionUtil.isNotEmpty(list)){
            //不为空则存在
            return true;
        }else{
            return false;
        }
    }

    @Override
    @GlobalTransactional // 重点 第一个开启事务的需要添加seata全局事务注解
    public AjaxResult insertDevice(IotDeviceInfo iotDeviceInfo) {
        //判断id是否为空
        if(StringUtils.isEmpty(iotDeviceInfo.getIotId())){
            //为空自动生成id
            String id = IdUtils.fastSimpleUUID();
            iotDeviceInfo.setIotId(id);
        }
        //判断id是否重复
        if(isExistsDeviceById(iotDeviceInfo.getIotId())){
            return AjaxResult.error("设备id重复");
        }
        int rows = insertIotDeviceInfo(iotDeviceInfo);
        if(rows > 0){
            //增设备时候放入设备缓存
            String key = CacheConstants.DEVICE_USEINFO_KEY+iotDeviceInfo.getIotId();
            deviceCacheService.getDevcieCacheByDbAndSetKey(key,iotDeviceInfo.getIotId());
            //新增设备子表
            deviceTddbService.createTdDevcieTable(iotDeviceInfo);
            //绑定设备任务模板
            deviceTaskService.createDeviceTaskJob(iotDeviceInfo);
        }
        return rows > 0 ? AjaxResult.success() : AjaxResult.error();
    }




    @Override
    @Transactional
    @GlobalTransactional
    public AjaxResult deleteDeviceByIds(String iotId) {
        //删除设备缓存信息
        deviceCacheService.deleteDeviceCacheById(iotId);
        //删除设备任务
        deviceTaskService.delteDeviceJob(iotId);
        //删除设备属性历史记录
        deviceTddbService.deleteDeviceTableById(iotId);
        //删除设备事件上报记录
        eventHistoryMapper.deleteIotEventHistoryByIotId(iotId);
        //删除设备服务调用记录
        serviceHistoryMapper.deleteIotServiceHistoryByIotId(iotId);
        //删除网关设备关联子设备关联关系topo
        deviceTopoMapper.deleteIotDeviceTopoByGatewayId(iotId);
        //删除设备信息
        deleteIotDeviceInfoByIotId(iotId);
        return AjaxResult.success();
    }



    @Override
    public List<IotDeviceInfo> selectDeviceInfoMyList(IotDeviceInfo iotDeviceInfo, String userId) {
        return iotDeviceInfoMapper.selectDeviceInfoMyList(iotDeviceInfo,userId);
    }

    @Override
    public List<IotDeviceInfo> selectDeviceInfoAuthList(IotDeviceInfo iotDeviceInfo, String userId) {
        return iotDeviceInfoMapper.selectDeviceInfoAuthList(iotDeviceInfo,userId);
    }

    @Override
    public List<Map<String, Object>> getPropertiesByIotId(String id) {
        return deviceTddbService.getPropertiesByIotId(id);
    }

    @Override
    public List<Map<String, Object>> getHisProperties(String id, String startTime, String endTime) {
        return deviceTddbService.getHisProperties(id,startTime,endTime);
    }

    /**
     * 发送设备升级信息
     * @param iotId
     * @param upgradeStrategyId
     */
    @Override
    public void sendDeviceUpgradeMsg(String iotId, String upgradeStrategyId) {
        //获取升级包信息
        R<UpgradeFirmware> upgradeFileRe= remoteUpgradeService.getUpgradeFileByStrategyId(Long.valueOf(upgradeStrategyId));
        //获取升级包
        UpgradeFirmware upgradeFirmware = upgradeFileRe.getData();
        sendDeviceUpgradeMsgByFile(iotId,upgradeFirmware);
    }

    /**
     * 设备服务调用
     * @param deviceServiceInvoke
     */
    @Override
    public void invokeDeviceService(DeviceServiceInvoke deviceServiceInvoke) {
        DeviceCache deviceCache = getDeviceCacheByIotId(deviceServiceInvoke.getIotId());
        if(deviceCache != null) {
            //发送协议
            String protocol = deviceCache.getProtocol();
            //根据不同协议和消息类型 生成发送内容
            CommandRequest commandRequest = createInvokeServiceData(deviceCache,protocol,deviceServiceInvoke);
            //发送消息给在线设备
            sendDeviceMsg(deviceCache,commandRequest);
            //插入服务调用记录
            insertDeviceServiceHis(deviceCache,deviceServiceInvoke);
        }
    }

    /**
     * 插入服务调用记录
     * @param deviceCache
     * @param deviceServiceInvoke
     */
    private void insertDeviceServiceHis(DeviceCache deviceCache, DeviceServiceInvoke deviceServiceInvoke) {
        IotServiceHistory serviceHistory = new IotServiceHistory();
        serviceHistory.setKeyId(deviceServiceInvoke.getMethod());
        serviceHistory.setProductId(deviceCache.getProductId());
        serviceHistory.setDeviceId(deviceCache.getIotId());
        serviceHistory.setDeviceName(deviceCache.getDeviceName());
        serviceHistory.setServiceName(deviceServiceInvoke.getMethod());
        serviceHistory.setInput(JSON.toJSONString(deviceServiceInvoke.getParams()));
        serviceHistory.setCreateBy("sys");
        serviceHistory.setStatus("0");
        serviceHistory.setCreateTime(new Date());
        serviceHistoryMapper.insertIotServiceHistory(serviceHistory);
    }

    /**
     * 生成发送设备服务调用消息格式
     * @param deviceCache
     * @param protocol
     * @param deviceServiceInvoke
     * @return
     */
    private CommandRequest createInvokeServiceData(DeviceCache deviceCache, String protocol,
                                                   DeviceServiceInvoke deviceServiceInvoke) {
        CommandRequest commandRequest = new CommandRequest();
        //MQTT调用 mqtt网关服务 写 接口
        if(protocol.equals(ProtocolConstans.MQTT.toLowerCase())){
            //发送data内容
            Map<String, AttributeInfo> dataInfo = new HashMap<>();
            //mqtt标准发送服务调用实体
            //mqtt发送消息格式
            String mqttJson = "";
            //服务调用topic
            String invokeTopic ="";
            //转换成调用参数
            Map<String, Object> params = new HashMap<>();
            if(CollectionUtil.isNotEmpty(deviceServiceInvoke.getParams())){
                for (int i = 0; i < deviceServiceInvoke.getParams().size(); i++) {
                    DeviceInvkeParams deviceInvkeParam = deviceServiceInvoke.getParams().get(i);
                    //转换值
                    params.put(deviceInvkeParam.getName(),DriverUtil.value(deviceInvkeParam.getDataType(),
                            deviceInvkeParam.getValues()));
                }
            }
            //判断是否子设备
            if(deviceCache.getNodeType() == 2){
                //子设备,需要子设备调用主网关设备id
                SubServiceInvoke subServiceInvoke = new SubServiceInvoke();
                subServiceInvoke.setMethod(deviceServiceInvoke.getMethod());
                subServiceInvoke.setId(IdUtils.fastSimpleUUID());
                subServiceInvoke.setProductKey(deviceCache.getProductKey());
                subServiceInvoke.setDeviceName(deviceCache.getIotId());

                subServiceInvoke.setInputParam(params);
                mqttJson = JSON.toJSONString(subServiceInvoke);

                //服务调用topic替换参数
                invokeTopic = MqTopic.NetTopic.SUB_SERVICE_INVOKE_FMT;
                //获取主设备信息
                DeviceCache gatteWayCache = getDeviceCacheByIotId(deviceCache.getGateWayId());
                invokeTopic = invokeTopic.replace("{productKey}",gatteWayCache.getProductKey());
                invokeTopic = invokeTopic.replace("{deviceName}",deviceCache.getGateWayId());

            }else{
                //网关,直连设备
                ServiceInvoke serviceInvoke = new ServiceInvoke();
                serviceInvoke.setMethod(deviceServiceInvoke.getMethod());
                serviceInvoke.setId(UUID.fastUUID().toString());

                serviceInvoke.setParams(params);
                mqttJson = JSON.toJSONString(serviceInvoke);
                //服务调用topic替换参数
                invokeTopic = MqTopic.NetTopic.SERVICE_INVOKE_FMT;
                invokeTopic = invokeTopic.replace("{productKey}",deviceCache.getProductKey());
                invokeTopic = invokeTopic.replace("{deviceName}",deviceCache.getIotId());
                invokeTopic = invokeTopic.replace("{identifier}",deviceServiceInvoke.getMethod());
            }
            AttributeInfo attributeInfo = new AttributeInfo();
            attributeInfo.setType("string");
            attributeInfo.setValue(mqttJson);
            dataInfo.put("data",attributeInfo);
            //发送topic
            AttributeInfo attributetopicInfo = new AttributeInfo();
            attributetopicInfo.setType("string");

            attributetopicInfo.setValue(invokeTopic);
            dataInfo.put("topic",attributetopicInfo);

            commandRequest.setDataInfo(dataInfo);
        }else{

        }
        return commandRequest;
    }


    /**
     * 发送设备升级信息
     *
     * @param iotId
     * @param upgradeFirmware
     */
    public void sendDeviceUpgradeMsgByFile(String iotId, UpgradeFirmware upgradeFirmware) {
        DeviceCache deviceCache = getDeviceCacheByIotId(iotId);
        if(deviceCache != null) {
            //发送协议
            String protocol = deviceCache.getProtocol();
            //根据不同协议和消息类型 生成发送内容
            CommandRequest commandRequest = createSendUpgradeData(deviceCache,protocol,upgradeFirmware);
            //发送消息给在线设备
            sendDeviceMsg(deviceCache,commandRequest);
        }
    }

    /**
     * 发送消息给设备
     * @param deviceCache
     * @param commandRequest
     */
    public void sendDeviceMsg(DeviceCache deviceCache, CommandRequest commandRequest) {
        //先不判断在线状态

        //发送协议
        String protocol = deviceCache.getProtocol();
        //MQTT调用 mqtt网关服务 写 接口
        if(protocol.equals(ProtocolConstans.MQTT.toLowerCase())){
            mqttFegin.write(commandRequest);
        }else{
            //其他协议 检查存在连接的ip和端口调用 写 接口
            //获取设备连接的信息
            String key = CacheConstants.GW_DEVICE_CONECT_KEY+deviceCache.getIotId();
            DeviceConectInfo conectInfo =redisService.getCacheObject(key);
            String targetIp = conectInfo.getIp();
            String url =targetIp + ValueConstant.Driver.COMMAND_URL_PREFIX+"/write";
            restTemplate.postForEntity(url,commandRequest, R.class);
        }

    }

    /**
     * 生成发送升级消息实体
     *
     * @param deviceCache
     * @param protocol
     * @param upgradeFirmware
     * @return
     */
    private CommandRequest createSendUpgradeData(DeviceCache deviceCache, String protocol,
                                                 UpgradeFirmware upgradeFirmware) {
        CommandRequest commandRequest = new CommandRequest();
        //MQTT调用 mqtt网关服务 写 接口
        if(protocol.equals(ProtocolConstans.MQTT.toLowerCase())){
            //发送data内容
            Map<String, AttributeInfo> dataInfo = new HashMap<>();
            //mqtt发送消息格式 TODO:标准升级策略传参数json
            String mqttJson = "{\n" +
                    "  \"id\": \""+ UUID.fastUUID() +"\",\n" +
                    "  \"deviceName\": \""+deviceCache.getIotId()+"\",\n" +
                    "  \"params\": {\n" +
                    "    \"url\": \""+upgradeFirmware.getDownloadUrl()+"\",\n" +
                    "    \"name\": \""+upgradeFirmware.getFileName()+"\"\n" +
                    "    \"version\": \""+upgradeFirmware.getVersion()+"\"\n" +
                    "  },\n" +
                    "  \"method\": \"thing.service.upgrade\"\n" +
                    "}";
            AttributeInfo attributeInfo = new AttributeInfo();
            attributeInfo.setType("string");
            attributeInfo.setValue(mqttJson);
            dataInfo.put("data",attributeInfo);
            //发送topic
            AttributeInfo attributetopicInfo = new AttributeInfo();
            attributetopicInfo.setType("string");
            //TODO:升级服务调用 固定 upgrade服务升级
            String invokeTopic = MqTopic.NetTopic.UPGRADE_SEND_FMT;
            invokeTopic = invokeTopic.replace("{productKey}",deviceCache.getProductKey());
            invokeTopic = invokeTopic.replace("{deviceName}",deviceCache.getIotId());

            attributetopicInfo.setValue(invokeTopic);
            dataInfo.put("topic",attributetopicInfo);

            commandRequest.setDataInfo(dataInfo);

        }else{
            //TODO:其他协议
        }
        return commandRequest;
    }

    /**
     * 查询网关设备的子设备
     * @param id
     * @return
     */
    @Override
    public List<IotDeviceInfo> getTopoDevice(String id) {
        return iotDeviceInfoMapper.selectTopoDeviceByIotId(id);
    }

    /**
     * 查询网关没有绑定的子设备列表
     * @param id
     * @return
     */
    @Override
    public List<IotDeviceInfo> getNoBindTopoDevice(String id) {
        return iotDeviceInfoMapper.getNoBindTopoDevice(id);
    }


    /**
     * 根据id判断设备是否存在
     * @param iotId
     * @return
     */
    private boolean isExistsDeviceById(String iotId) {
        IotDeviceInfo deviceInfo = iotDeviceInfoMapper.selectIotDeviceInfoByIotId(iotId);
        if(deviceInfo != null){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 根据项目id查询设备列表
     * @param id
     * @return
     */
    public List<IotDeviceInfo> selectIotDeviceInfoByProjectId(Long id) {
        return iotDeviceInfoMapper.selectIotDeviceInfoByProjectId(id);
    }

}
